#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>
#include <time.h>

#define WIDTH 40
#define HEIGHT 20
#define SNAKE_MAX_LEN 100
#define SAVE_FILE "save.txt"

void gotoxy(int x, int y)
{
    COORD pos = {x * 2, y};
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}

void printsnake()
{
    printf("\n\n");
    printf("        *****\n");
    printf("       *     *\n");
    printf("      *  0 0  *\n");
    printf("      *   V   *\n");
    printf("       * --- *\n");
    printf("        *****\n");
}

void welcometogame()
{
    system("cls");
    printsnake();
    printf("\n\n");
    printf("    1. 开始游戏\n");
    printf("    2. 游戏说明\n");
    printf("    3. 显示最高分\n");
    printf("    4. 退出游戏\n");
    printf("\n请选择菜单项(1-4): ");
}

void createMap()
{
    int i, j;
    for (i = 0; i <= HEIGHT; i++)
    {
        for (j = 0; j <= WIDTH; j++)
        {
            if (i == 0 || i == HEIGHT || j == 0 || j == WIDTH)
                printf("■");
            else
                printf("  ");
        }
        printf("\n");
    }
}

typedef struct
{
    int a, b;
} P;

typedef struct
{
    P arr[SNAKE_MAX_LEN];
    int len;
    int d;
} S;

S s;
P f;
int sc = 0;   
int hs = 0;   
int st = 200;
int run = 1;  

void scoreandtips()
{
    gotoxy(WIDTH + 2, 2);
    printf("当前得分: %d", sc);
    gotoxy(WIDTH + 2, 4);
    printf("最高得分: %d", hs);
    gotoxy(WIDTH + 2, 6);
    printf("F1: 加速  F2: 减速");
    gotoxy(WIDTH + 2, 8);
    printf("ESC: 退出");
}

void initsnake()
{
    int i;
    s.len = 5;
    s.d = 3;
    for (i = 0; i < s.len; i++)
    {
        s.arr[i].a = 10 - i;
        s.arr[i].b = 5;
    }
}

void drawsnake()
{
    int i;
    for (i = 0; i < s.len; i++)
    {
        gotoxy(s.arr[i].a, s.arr[i].b);
        printf("●");
    }
}

void clearsnake()
{
    gotoxy(s.arr[s.len - 1].a, s.arr[s.len - 1].b);
    printf("  ");
}

void snakemove()
{
    int i;
    for (i = s.len - 1; i > 0; i--)
        s.arr[i] = s.arr[i - 1];
    switch (s.d)
    {
    case 0:
        s.arr[0].b--;
        break;
    case 1:
        s.arr[0].b++;
        break;
    case 2:
        s.arr[0].a--;
        break;
    case 3:
        s.arr[0].a++;
        break;
    }
}

int isOnSnake(int x, int y)
{
    int i;
    for (i = 0; i < s.len; i++)
    {
        if (s.arr[i].a == x && s.arr[i].b == y)
            return 1;
    }
    return 0;
}

void createfood()
{
    int x, y;
    do
    {
        x = rand() % (WIDTH - 1) + 1;
        y = rand() % (HEIGHT - 1) + 1;
    } while (isOnSnake(x, y));
    f.a = x;
    f.b = y;
    gotoxy(f.a, f.b);
    printf("◆");
}

int biteself()
{
    int i;
    for (i = 1; i < s.len; i++)
    {
        if (s.arr[0].a == s.arr[i].a && s.arr[0].b == s.arr[i].b)
            return 1;
    }
    return 0;
}

int cantcrosswall()
{
    if (s.arr[0].a == 0 || s.arr[0].a == WIDTH ||
        s.arr[0].b == 0 || s.arr[0].b == HEIGHT)
        return 1;
    return 0;
}

void speedup()
{
    if (st > 50)
        st -= 10;
}

void speeddown()
{
    if (st < 350)
        st += 30;
}

void keyboardControl()
{
    if (_kbhit())
    {
        int key = _getch();
        if (key == 224)
        {
            key = _getch();
            switch (key)
            {
            case 72:
                if (s.d != 1)
                    s.d = 0;
                break; 
            case 80:
                if (s.d != 0)
                    s.d = 1;
                break; 
            case 75:
                if (s.d != 3)
                    s.d = 2;
                break; 
            case 77:
                if (s.d != 2)
                    s.d = 3;
                break; 
            }
        }
        else if (key == 27) 
        {
            run = 0;
        }
        else if (key == 0 || key == 224)
        {
            key = _getch();
            if (key == 59)
                speedup(); 
            if (key == 60)
                speeddown(); 
        }
    }
}

void File_out()
{
    FILE *fp = fopen(SAVE_FILE, "r");
    if (fp)
    {
        fscanf(fp, "%d", &hs);
        fclose(fp);
    }
    else
    {
        hs = 0;
    }
}

void File_in()
{
    FILE *fp = fopen(SAVE_FILE, "w");
    if (fp)
    {
        fprintf(fp, "%d", hs);
        fclose(fp);
    }
}

void explation()
{
    system("cls");
    printf("贪吃蛇游戏说明：\n");
    printf("1. 通过方向键控制蛇的移动。\n");
    printf("2. 吃到食物蛇会变长并加分。\n");
    printf("3. 撞墙或咬到自己游戏结束。\n");
    printf("4. F1加速，F2减速，ESC退出。\n");
    printf("按任意键返回主菜单...\n");
    _getch();
}

void showHighScore()
{
    system("cls");
    printf("最高分: %d\n", hs);
    printf("按任意键返回主菜单...\n");
    _getch();
}

void gameover()
{
    system("cls");
    if (sc > hs)
    {
        hs = sc;
        File_in();
        printf("创纪录啦！最高分被你刷新啦，真棒！！！\n");
    }
    else
    {
        printf("继续努力吧~你离最高分还差: %d\n", hs - sc);
    }
    printf("1. 重玩\n2. 退出游戏\n请选择(1/2): ");
    int ch;
    while (1)
    {
        ch = _getch();
        if (ch == '1')
            return;
        else if (ch == '2')
            exit(0);
    }
}

void game()
{
    system("cls");
    createMap();
    File_out();
    scoreandtips();
    initsnake();
    drawsnake();
    createfood();
    while (run)
    {
        clearsnake(); 
        snakemove();
        if (cantcrosswall() || biteself())
        {
            run = 0;
            break;
        }
        if (s.arr[0].a == f.a && s.arr[0].b == f.b)
        {
            s.len++;
            sc += 10;
            scoreandtips();
            createfood();
        }
        drawsnake();
        scoreandtips();
        Sleep(st);
        keyboardControl();
    }
    gameover();
}

int main()
{
    int choice;
    srand((unsigned)time(NULL)); 
    File_out();
    while (1)
    {
        welcometogame();
        choice = _getch();
        switch (choice)
        {
        case '1':
            sc = 0;
            st = 200;
            run = 1;
            game();
            break;
        case '2':
            explation();
            break;
        case '3':
            showHighScore();
            break;
        case '4':
            exit(0);
        }
    }
    return 0;
}